package com.carrotsearch.sizeof; /** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import java.lang.reflect.Field; import java.nio.ByteOrder; import java.util.Locale; import java.util.Map; import java.util.TreeMap; import sun.misc.Unsafe; /** * This class contains black magic stuff based mostly on proprietary * APIs and hacks. Use at your own risk. */ @SuppressWarnings({"restriction"}) public final class BlackMagic { /** * Returns Unsafe if available or throw a RuntimeException. */ public static sun.misc.Unsafe getUnsafe() { try { final Class<?> unsafeClass = Class.forName("sun.misc.Unsafe"); final Field unsafeField = unsafeClass.getDeclaredField("theUnsafe"); unsafeField.setAccessible(true); return (sun.misc.Unsafe) unsafeField.get(null); } catch (Throwable t) { throw new RuntimeException("Unsafe not available.", t); } } /** * Attempts to dump physical object's memory as a string. */ public static String objectMemoryAsString(Object o) { final Unsafe unsafe = getUnsafe(); final ByteOrder byteOrder = ByteOrder.nativeOrder(); StringBuilder b = new StringBuilder(); final int obSize = (int) RamUsageEstimator.shallowSizeOf(o); for (int i = 0; i < obSize; i += 2) { if ((i & 0xf) == 0) { if (i > 0) b.append("\n"); b.append(String.format(Locale.ENGLISH, "%#06x", i)); } // we go short by short because J9 fails on odd addresses (everything is aligned, // including byte fields. int shortValue = unsafe.getShort(o, (long) i); if (byteOrder == ByteOrder.BIG_ENDIAN) { b.append(String.format(Locale.ENGLISH, " %02x", (shortValue >>> 8) & 0xff)); b.append(String.format(Locale.ENGLISH, " %02x", (shortValue & 0xff))); } else { b.append(String.format(Locale.ENGLISH, " %02x", (shortValue & 0xff))); b.append(String.format(Locale.ENGLISH, " %02x", (shortValue >>> 8) & 0xff)); } } return b.toString(); } /** * Attempts to dump a layout of a class's fields in memory * (offsets from base object pointer). */ @SuppressWarnings({"unchecked"}) public static String fieldsLayoutAsString(Class<?> clazz) { Unsafe unsafe = getUnsafe(); TreeMap<Long, String> fields = new TreeMap<Long, String>(); for (Class<?> c = clazz; c != null; c = c.getSuperclass()) { for (Field f : c.getDeclaredFields()) { fields.put( unsafe.objectFieldOffset(f), f.getDeclaringClass().getSimpleName() + "." + f.getName()); } } fields.put( RamUsageEstimator.shallowSizeOfInstance(clazz), "#shallowSizeOfInstance(" + clazz.getName() + ")"); StringBuilder b = new StringBuilder(); Object [] entries = fields.entrySet().toArray(); for (int i = 0; i < entries.length; i++) { Map.Entry<Long, String> e = (Map.Entry<Long, String>) entries[i]; Map.Entry<Long, String> next = (i + 1 < entries.length ? (Map.Entry<Long, String>) entries[i + 1] : null); b.append(String.format(Locale.ENGLISH, "@%02d %2s %s\n", e.getKey(), next == null ? "" : next.getKey() - e.getKey(), e.getValue())); } return b.toString(); } }